#if !defined(IMGLIB_COLOR_H) 
#define IMGLIB_COLOR_H

#include "typelib/operations.h"
#include "imageclass_decl.h"
#include "math/round.h"
#include "type_traits.h"

#include <iostream>

namespace img {

template <class T> class BGRA;
template <class T> class BGR;
template <class T> class RGBA;
template <class T> class ARGB;

template <class T> class BGRA {
public:
	typedef T component_type;
public:
	T	b, g, r, a;
public:
	BGRA(T _b = T(), T _g = T(), T _r = T(), T _a = T())
		: b(_b), g(_g), r(_r), a(_a) { }
	BGRA(const BGRA & rhs)
		: b(rhs.b), g(rhs.g), r(rhs.r), a(rhs.a) { }

	template <class U> BGRA(const BGRA<U> & rhs)
		: b(T(rhs.b)), g(T(rhs.g)), r(T(rhs.r)), a(T(rhs.a)) { }
	template <class U> BGRA(const BGR<U> & rhs)
		: b(T(rhs.b)), g(T(rhs.g)), r(T(rhs.r)), a() { }
	template <class U> BGRA(const RGBA<U> & rhs)
		: b(T(rhs.b)), g(T(rhs.g)), r(T(rhs.r)), a(T(rhs.a)) { }
	template <class U> BGRA(const ARGB<U> & rhs)
		: b(T(rhs.b)), g(T(rhs.g)), r(T(rhs.r)), a(T(rhs.a)) { }
	
	template <class U> BGRA & operator = (const BGRA<U> & rhs) 
		{ return * this = BGRA(T(rhs.b), T(rhs.g), T(rhs.r), T(rhs.a)); }
	template <class U> BGRA & operator = (const BGR<U> & rhs) 
		{ return * this = BGRA(T(rhs.b), T(rhs.g), T(rhs.r), T()); }
	template <class U> BGRA & operator = (const RGBA<U> & rhs) 
		{ return * this = BGRA(T(rhs.b), T(rhs.g), T(rhs.r), T(rhs.a)); }
	template <class U> BGRA & operator = (const ARGB<U> & rhs) 
		{ return * this = BGRA(T(rhs.b), T(rhs.g), T(rhs.r), T(rhs.a)); }

	bool operator == (const BGRA & rhs) const
		{ return (b == rhs.b && g == rhs.g && r == rhs.r && a == rhs.a); }
	bool operator != (const BGRA & rhs) const
		{ return (b != rhs.b || g != rhs.g || r != rhs.r || a != rhs.a); }
	template <class U>
	BGRA & operator += (const BGRA<U> & rhs) { 
		b += T(rhs.b);
		g += T(rhs.g);
		r += T(rhs.r);
		a += T(rhs.a);
		return * this;
	}
	template <class U>
	BGRA & operator -= (const BGRA<U> & rhs) { 
		b -= T(rhs.b);
		g -= T(rhs.g);
		r -= T(rhs.r);
		a -= T(rhs.a);
		return * this;
	}
	BGRA & operator *= (T c) {
		b *= c;
		g *= c;
		r *= c;
		a *= c;
		return * this;
	}
	BGRA & operator /= (T c) {
		b /= c;
		g /= c;
		r /= c;
		a /= c;
		return * this;
	}
	unsigned sqr() const
		{ return b*b + g*g + r*r; }
	double fsqr() const
		{ return b*b + g*g + r*r; }

	bool is_black() const
		{ return b==0 && g==0 && r==0; }

	friend BGRA operator + (const BGRA & lhs, const BGRA & rhs) {
		BGRA res(lhs);
		res += rhs;
		return res;
	}
	friend BGRA operator - (const BGRA & lhs, const BGRA & rhs) {
		BGRA res(lhs);
		res -= rhs;
		return res;
	}
	friend BGRA operator - (const BGRA & c)
		{ return BGRA()-c; }
	friend BGRA operator * (const BGRA & lhs, T c) {
		BGRA res(lhs);
		res *= c;
		return res;
	}
	friend BGRA operator * (T c, const BGRA & lhs) {
		BGRA res(lhs);
		res *= c;
		return res;
	}
	friend BGRA operator / (const BGRA & lhs, T c) {
		BGRA res(lhs);
		res /= c;
		return res;
	}
	template <class U>
	friend bool operator < (const BGRA<U> & lhs, const BGRA<U> & rhs);
};

template <class T> 
inline bool operator < (const BGRA<T> & lhs, const BGRA<T> & rhs) {
	return lhs.a < rhs.a || lhs.a == rhs.a &&
		(lhs.r < rhs.r || lhs.r == rhs.r &&
			(lhs.g < rhs.g || lhs.g == rhs.g &&
				lhs.b < rhs.b));
}

template <>
inline bool operator < (const BGRA<unsigned char> & lhs, const BGRA<unsigned char> & rhs) {
	return reinterpret_cast<const unsigned&>(lhs) < reinterpret_cast<const unsigned&>(rhs);
}

template <class T> class BGR {
public:
	typedef T component_type;
public:
	T	b, g, r;
public:
	BGR(T _b = T(), T _g = T(), T _r = T())
		: b(_b), g(_g), r(_r) { }
	BGR(const BGR & rhs)
		: b(rhs.b), g(rhs.g), r(rhs.r) { }

	template <class U> BGR(const BGRA<U> & rhs)
		: b(T(rhs.b)), g(T(rhs.g)), r(T(rhs.r)) { }
	template <class U> BGR(const BGR<U> & rhs)
		: b(T(rhs.b)), g(T(rhs.g)), r(T(rhs.r)) { }
	template <class U> BGR(const RGBA<U> & rhs)
		: b(T(rhs.b)), g(T(rhs.g)), r(T(rhs.r)) { }
	template <class U> BGR(const ARGB<U> & rhs)
		: b(T(rhs.b)), g(T(rhs.g)), r(T(rhs.r)) { }

	BGR & operator = (const BGR & rhs) 
		{ b = rhs.b; g = rhs.g; r = rhs.r; return *this; }

	template <class U> BGR & operator = (const BGRA<U> & rhs) 
		{ return * this = BGR<T>(rhs.b, rhs.g, rhs.r); }
	template <class U> BGR & operator = (const BGR<U> & rhs) 
		{ return * this = BGR<T>(rhs.b, rhs.g, rhs.r); }
	template <class U> BGR & operator = (const RGBA<U> & rhs) 
		{ return * this = BGR<T>(rhs.b, rhs.g, rhs.r); }
	template <class U> BGR & operator = (const ARGB<U> & rhs) 
		{ return * this = BGR<T>(rhs.b, rhs.g, rhs.r); }

	bool operator == (const BGR & rhs) const
		{ return (b == rhs.b && g == rhs.g && r == rhs.r); }
	bool operator != (const BGR & rhs) const
		{ return (b != rhs.b || g != rhs.g || r != rhs.r); }
	BGR & operator += (const BGR & rhs) { 
		b += rhs.b;
		g += rhs.g;
		r += rhs.r;
		return * this;
	}
	BGR & operator -= (const BGR & rhs) { 
		b -= rhs.b;
		g -= rhs.g;
		r -= rhs.r;
		return * this;
	}
	BGR & operator *= (T c) {
		b *= c;
		g *= c;
		r *= c;
		return * this;
	}
	BGR & operator /= (T c) {
		b /= c;
		g /= c;
		r /= c;
		return * this;
	}

	bool is_black() const
		{ return b==0 && g==0 && r==0; }
	template <class T>
	T sqr() const
		{ return T(b)*T(b) + T(g)*T(g) + T(r)*T(r); }

	friend BGR operator + (const BGR & lhs, const BGR & rhs) {
		BGR res(lhs);
		res += rhs;
		return res;
	}
	friend BGR operator - (const BGR & lhs, const BGR & rhs) {
		BGR res(lhs);
		res -= rhs;
		return res;
	}
	friend BGR operator - (const BGR & c)
		{ return BGR()-c; }
	friend BGR operator * (const BGR & lhs, T c) {
		BGR res(lhs);
		res *= c;
		return res;
	}
	friend BGR operator * (T c, const BGR & lhs) {
		BGR res(lhs);
		res *= c;
		return res;
	}
	friend BGR operator / (const BGR & lhs, T c) {
		BGR res(lhs);
		res /= c;
		return res;
	}
	friend bool operator < (const BGR & lhs, const BGR & rhs) {
		return 
			(lhs.r < rhs.r || lhs.r == rhs.r &&
				(lhs.g < rhs.g || lhs.g == rhs.g &&
					lhs.b < rhs.b));
	}
};

template <class T> class RGBA {
public:
	typedef T component_type;
public:
	T	r, g, b, a;
public:
	RGBA(T _r = T(), T _g = T(), T _b = T(), T _a = T())
		: r(_r), g(_g), b(_b), a(_a) { }
	RGBA(const RGBA & rhs)
		: r(rhs.r), g(rhs.g), b(rhs.b), a(rhs.a) { }

	template <class U> RGBA(const BGRA<U> & rhs)
		: r(T(rhs.r)), g(T(rhs.g)), b(T(rhs.b)), a(T(rhs.a)) { }
	template <class U> RGBA(const BGR<U> & rhs)
		: b(T(rhs.b)), g(T(rhs.g)), r(T(rhs.r)), a() { }
	template <class U> RGBA(const RGBA<U> & rhs)
		: r(T(rhs.r)), g(T(rhs.g)), b(T(rhs.b)), a(T(rhs.a)) { }

	template <class U> RGBA & operator = (const BGRA<U> & rhs) 
		{ return * this = RGBA(rhs.r, rhs.g, rhs.b, rhs.a); }
	template <class U> RGBA & operator = (const BGR<U> & rhs) 
		{ return * this = RGBA(rhs.r, rhs.g, rhs.b, T()); }
	template <class U> RGBA & operator = (const RGBA<U> & rhs) 
		{ return * this = RGBA(rhs.r, rhs.g, rhs.b, rhs.a); }

	bool operator == (const RGBA & rhs) const
		{ return (r == rhs.r && g == rhs.g && b == rhs.b && a == rhs.a); }
	bool operator != (const RGBA & rhs) const
		{ return (r != rhs.r || g != rhs.g || b != rhs.b || a != rhs.a); }
	RGBA & operator += (const RGBA & rhs) { 
		r += rhs.r;
		g += rhs.g;
		b += rhs.b;
		a += rhs.a;
		return * this;
	}
	RGBA & operator -= (const RGBA & rhs) { 
		r -= rhs.r;
		g -= rhs.g;
		b -= rhs.b;
		a -= rhs.a;
		return * this;
	}
	RGBA & operator *= (T c) {
		r *= c;
		g *= c;
		b *= c;
		a *= c;
		return * this;
	}
	RGBA & operator /= (T c) {
		r /= c;
		g /= c;
		b /= c;
		a /= c;
		return * this;
	}

	bool is_black() const
		{ return b==0 && g==0 && r==0; }

	friend RGBA operator + (const RGBA & lhs, const RGBA & rhs) {
		RGBA res(lhs);
		res += rhs;
		return res;
	}
	friend RGBA operator - (const RGBA & lhs, const RGBA & rhs) {
		RGBA res(lhs);
		res -= rhs;
		return res;
	}
	friend RGBA operator - (const RGBA & c)
		{ return RGBA()-c; }
	friend RGBA operator * (const RGBA & lhs, T c) {
		RGBA res(lhs);
		res *= c;
		return res;
	}
	friend RGBA operator * (T c, const RGBA & lhs) {
		RGBA res(lhs);
		res *= c;
		return res;
	}
	friend RGBA operator / (const RGBA & lhs, T c) {
		RGBA res(lhs);
		res /= c;
		return res;
	}
};

template <class T> class ARGB {
public:
	typedef T component_type;
public:
	T	a, r, g, b;
public:
	ARGB(T _a = T(), T _r = T(), T _g = T(), T _b = T())
		: r(_r), g(_g), b(_b), a(_a) { }
	ARGB(const ARGB & rhs)
		: r(rhs.r), g(rhs.g), b(rhs.b), a(rhs.a) { }

	template <class U> ARGB(const BGRA<U> & rhs)
		: r(T(rhs.r)), g(T(rhs.g)), b(T(rhs.b)), a(T(rhs.a)) { }
	template <class U> ARGB(const BGR<U> & rhs)
		: b(T(rhs.b)), g(T(rhs.g)), r(T(rhs.r)), a() { }
	template <class U> ARGB(const ARGB<U> & rhs)
		: r(T(rhs.r)), g(T(rhs.g)), b(T(rhs.b)), a(T(rhs.a)) { }

	template <class U> ARGB & operator = (const BGRA<U> & rhs) 
		{ return * this = ARGB(rhs.a, rhs.r, rhs.g, rhs.b); }
	template <class U> ARGB & operator = (const BGR<U> & rhs) 
		{ return * this = ARGB(T(), rhs.r, rhs.g, rhs.b); }
	template <class U> ARGB & operator = (const ARGB<U> & rhs) 
		{ return * this = ARGB(rhs.a, rhs.r, rhs.g, rhs.b); }

	bool operator == (const ARGB & rhs) const
		{ return (r == rhs.r && g == rhs.g && b == rhs.b && a == rhs.a); }
	bool operator != (const ARGB & rhs) const
		{ return (r != rhs.r || g != rhs.g || b != rhs.b || a != rhs.a); }
	ARGB & operator += (const ARGB & rhs) { 
		r += rhs.r;
		g += rhs.g;
		b += rhs.b;
		a += rhs.a;
		return * this;
	}
	ARGB & operator -= (const ARGB & rhs) { 
		r -= rhs.r;
		g -= rhs.g;
		b -= rhs.b;
		a -= rhs.a;
		return * this;
	}
	ARGB & operator *= (T c) {
		r *= c;
		g *= c;
		b *= c;
		a *= c;
		return * this;
	}
	ARGB & operator /= (T c) {
		r /= c;
		g /= c;
		b /= c;
		a /= c;
		return * this;
	}

	bool is_black() const
		{ return b==0 && g==0 && r==0; }

	friend ARGB operator + (const ARGB & lhs, const ARGB & rhs) {
		ARGB res(lhs);
		res += rhs;
		return res;
	}
	friend ARGB operator - (const ARGB & lhs, const ARGB & rhs) {
		ARGB res(lhs);
		res -= rhs;
		return res;
	}
	friend ARGB operator - (const ARGB & c)
		{ return ARGB()-c; }
	friend ARGB operator * (const ARGB & lhs, T c) {
		ARGB res(lhs);
		res *= c;
		return res;
	}
	friend ARGB operator * (T c, const ARGB & lhs) {
		ARGB res(lhs);
		res *= c;
		return res;
	}
	friend ARGB operator / (const ARGB & lhs, T c) {
		ARGB res(lhs);
		res /= c;
		return res;
	}
};

inline byte_color discrete0(const float_color & c) 
	{ return byte_color(
		round(c.b), 
		round(c.g), 
		round(c.r),
		round(c.a)); }

inline small_byte_color discrete0(const small_float_color & c) 
	{ return byte_color(
		round(c.b), 
		round(c.g), 
		round(c.r)); }

template<class T>
inline T limit( T val, T mn, T mx ) { 
	return (val < mn) ? mn : ((val > mx) ? mx : val);
}

inline small_byte_color discrete(const small_float_color & c) 
	{ return byte_color(
		limit(round(c.b),0,255), 
		limit(round(c.g),0,255), 
		limit(round(c.r),0,255)); }

template <class T> struct float_interpreter;

template <>
struct float_interpreter<byte_color> {
	float_color operator ()(const byte_color & t) const
		{ return t; }
};

} // img

template <class T> 
inline double scalar_product(const img::BGRA<T> & x, const img::BGRA<T> & y)
	{ return double(x.b * y.b + x.g * y.g + x.r * y.r + x.a * y.a); }

template <class T> 
inline void inverse(img::BGRA<T> & x)
	{ inverse(x.b); inverse(x.g); inverse(x.r); inverse(x.a); }

template <class T> 
inline double scalar_product(const img::BGR<T> & x, const img::BGR<T> & y)
	{ return double(x.b * y.b + x.g * y.g + x.r * y.r); }

template <class T> 
inline void inverse(img::BGR<T> & x)
	{ inverse(x.b); inverse(x.g); inverse(x.r); }

template <class T> 
inline double scalar_product(const img::RGBA<T> & x, const img::RGBA<T> & y)
	{ return double(x.r * y.r + x.g * y.g + x.b * y.b + x.a * y.a); }

template <class T> 
inline void inverse(img::RGBA<T> & x)
	{ inverse(x.r); inverse(x.g); inverse(x.b); inverse(x.a); }

template <>
inline byte_color linear(const byte_color & a0, const byte_color & a1, float a)
{ 
	float_color fa0(a0), fa1(a1);
	return img::discrete0(linear(fa0, fa1, a));
}

template <>
inline byte_color bilinear(const byte_color & a00, const byte_color & a01, 
	const byte_color & a10, const byte_color & a11, float a0, float a1)
{ 
	float_color fa00(a00), fa01(a01), fa10(a10), fa11(a11);
	return img::discrete0(bilinear(fa00, fa01, fa10, fa11, a0, a1));
}


inline std::istream & operator >> (std::istream & in, byte_color & c) {
	int r, g, b;
	in >> r >> g >> b;
	c.r = r; c.g = g; c.b = b;
	return in;
}

inline std::ostream & operator << (std::ostream & out, byte_color const & c)
	{ return out << (int)c.r << ' ' << (int)c.g << ' ' << (int)c.b; }

#endif // IMGLIB_COLOR_H
